/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.atomojo.www.util.script;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.atomojo.app.client.Link;
import org.atomojo.app.client.LinkSet;
import org.atomojo.www.util.Identity;
import org.atomojo.www.util.ResourceManager;
import org.restlet.Application;
import org.restlet.Client;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.CharacterSet;
import org.restlet.data.Cookie;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.Method;
import org.restlet.data.Parameter;
import org.restlet.data.Status;
import org.restlet.representation.OutputRepresentation;
import org.restlet.representation.Representation;

/**
 *
 * @author alex
 */
public class IndexApplication extends Application {

   public static final String LINKS_ATTR = "org.atomojo.www.app.links";
   final static String T_APP_RESOURCE = "http://www.atomojo.org/O/www/configuration/application/resource";
   final static String T_APP_PREFIX   = "http://www.atmoo.org/O/www/configuration/application/prefix";
   
   ScriptManager manager;
   Link base;
   String resourceName;
   String prefix;
   public IndexApplication(Context context)
   {
      super(context);
      getTunnelService().setEnabled(false);
      manager = (ScriptManager)context.getAttributes().get(ScriptManager.ATTR);
      LinkSet linkset = (LinkSet)context.getAttributes().get(LINKS_ATTR);
      String relation = context.getParameters().getFirstValue("http://www.atomojo.org/O/www/configuration/application/link-relation");
      if (relation==null) {
         relation = "resources";
      }
      List<Link> links = linkset.get(relation);
      if (links!=null && links.size()>0) {
         base = links.get(0);
      }
      if (base==null) {
         getLogger().severe("The link relation "+relation+" cannot be found in the context.");
      }
      resourceName = context.getParameters().getFirstValue(T_APP_RESOURCE);
      prefix = context.getParameters().getFirstValue(T_APP_PREFIX);
      getLogger().info("Prefix: "+prefix);
      getConnectorService().getClientProtocols().add(Protocol.HTTPS);
      getConnectorService().getClientProtocols().add(Protocol.HTTP);
      getConnectorService().getClientProtocols().add(Protocol.FILE);
      getConnectorService().getClientProtocols().add(Protocol.CLAP);
      getConnectorService().getClientProtocols().add(Protocol.RIAP);
   }
   
   public void setResource(String name) {
      this.resourceName = null;
   }
   
   public Restlet createRoot() {
      return new Restlet(getContext()) {
         boolean isFineLog = getLogger().isLoggable(Level.FINE);
         public void handle(final Request request,Response response) {
            if (!request.getMethod().equals(Method.GET)) {
               response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED);
               return;
            }
            if (base==null) {
               response.setStatus(Status.CLIENT_ERROR_PRECONDITION_FAILED);
               return;
            }
            Object o = request.getAttributes().get("path");
            String path = prefix==null ? "" : prefix;
            if (o!=null) {
               path = path+o.toString();
            }
            String remaining = request.getResourceRef().getRemainingPart();
            if (remaining!=null && remaining.length()>0) {
               if (path.length()>0) {
                  path += "/"+remaining;
               } else {
                  path = remaining;
               }
            }
            if (path.length()>0 && path.charAt(0)=='/') {
               path = path.substring(1);
            }
            final Reference resource = new Reference(base.getLink().toString()+path);
            if (path.length()>0 && path.charAt(path.length()-1)!='/') {
               // resource
               if (isFineLog) {
                  getLogger().fine("Proxy to "+resource);
               }
               Client client = getContext().getClientDispatcher();
               //Client client = new Client(getContext().createChildContext(),Protocol.valueOf(resource.getScheme()));
               client.getContext().getAttributes().put("hostnameVerifier", org.apache.commons.ssl.HostnameVerifier.DEFAULT);

               Request remoteRequest = new Request(request.getMethod(),resource);
               if (request.isEntityAvailable()) {
                  remoteRequest.setEntity(request.getEntity());
               }
               if (base.getUsername()!=null) {
                  remoteRequest.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,base.getUsername(),base.getPassword()));
               }

               Response remoteResponse = client.handle(remoteRequest);

               response.setStatus(remoteResponse.getStatus());
               response.setEntity(remoteResponse.getEntity());
            } else {
               ScriptManager.ScriptContext releaseScript = null;
               try {
                  final ScriptManager.ScriptContext script = manager.findScript(request,path);
                  if (script==null) {
                     response.setStatus(Status.CLIENT_ERROR_NOT_FOUND);
                     return;
                  }
                  releaseScript = script;

                  Response remoteResponse = null;
                  if (resourceName!=null) {
                     if (isFineLog) {
                        getLogger().fine("Using resource "+resourceName);
                     }
                     ResourceManager manager = (ResourceManager)getContext().getAttributes().get(ResourceManager.ATTR);
                     ResourceManager.Retriever retriever = manager.findResource(resourceName);
                     if (retriever!=null) {
                        remoteResponse = retriever.get();
                     } else {
                        getLogger().warning("No resource named "+resourceName);
                     }
                  } else {
                     Client client = getContext().getClientDispatcher();
                     //Client client = new Client(getContext().createChildContext(),resource.getSchemeProtocol());
                     client.getContext().getAttributes().put("hostnameVerifier", org.apache.commons.ssl.HostnameVerifier.DEFAULT);
                     Request remoteRequest = new Request(Method.GET,new Reference(resource.toString()));
                     if (base.getUsername()!=null) {
                        remoteRequest.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,base.getUsername(),base.getPassword()));
                     }
                     Cookie cookie = request.getCookies().getFirst("I");
                     if (cookie!=null) {
                        remoteRequest.getCookies().add(cookie);
                     }
                     remoteResponse = client.handle(remoteRequest);
                  }
                  if (remoteResponse!=null && remoteResponse.getStatus().isSuccess()) {
                     final Representation feedResource = remoteResponse.getEntity();
                     response.setStatus(Status.SUCCESS_OK);
                     final Identity identity = (Identity)request.getAttributes().get(Identity.IDENTITY_ATTR);
                     final Map<String,Object> attrs = request.getAttributes();
                     releaseScript = null;
                     Representation rep = new OutputRepresentation(script.getMediaType()) {
                        public void write(OutputStream os)
                           throws IOException
                        {
                           try {
                              Transformer xform = script.getTransformer();
                              xform.clearParameters();
                              if (identity!=null) {
                                 xform.setParameter("user.session",identity.getSession());
                                 xform.setParameter("user.id",identity.getId());
                                 xform.setParameter("user.alias",identity.getAlias());
                                 if (identity.getName()!=null) {
                                    xform.setParameter("user.name",identity.getName());
                                 }
                                 if (identity.getEmail()!=null) {
                                    xform.setParameter("user.email",identity.getEmail());
                                 }
                              }
                              xform.setParameter("request.resource.path",request.getResourceRef().getPath());
                              xform.setParameter("request.resource.authority",request.getResourceRef().getAuthority());
                              xform.setParameter("request.resource.scheme",request.getResourceRef().getScheme());
                              xform.setParameter("request.resource.query",request.getResourceRef().getQuery());
                              xform.setParameter("request.resource.fragment",request.getResourceRef().getFragment());
                              for (String name : attrs.keySet()) {
                                 Object value = attrs.get(name);
                                 if (value instanceof String) {
                                    xform.setParameter(name,value);
                                 }
                              }
                              for (Parameter param : getContext().getParameters()) {
                                 xform.setParameter(param.getName(),param.getValue());
                              }
                              xform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
                              Reader input = null;
                              try {
                                 input = feedResource.getReader();
                                 try {
                                    xform.transform(new StreamSource(input,resource.toString()),new StreamResult(new OutputStreamWriter(os,"UTF-8")));  
                                 } catch (TransformerException ex) {
                                    getLogger().log(Level.SEVERE,"Cannot transform feed due to script error.",ex);
                                 }
                              } finally {
                                 if (input!=null) {
                                    input.close();
                                 }
                              }
                           } finally {
                              feedResource.release();
                           }
                        }
                        public void release() {
                           script.release();
                        }
                     };
                     rep.setCharacterSet(CharacterSet.UTF_8);
                     response.setEntity(rep);
                     response.setStatus(Status.SUCCESS_OK);
                  } else if (remoteResponse!=null) {
                     if (remoteResponse.isEntityAvailable()) {
                        remoteResponse.getEntity().release();
                     }
                     response.setStatus(remoteResponse.getStatus());
                  } else {
                     response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
                  }
               } catch (Exception ex) {
                  getLogger().log(Level.SEVERE,"Cannot process script for "+path+" due to exception.",ex);
                  response.setStatus(Status.SERVER_ERROR_INTERNAL);
               } finally {
                  if (releaseScript!=null) {
                     releaseScript.release();
                  }
               }
            }
         }
      };
   }}
